package org.tlang.ast.list.statement;

import org.tlang.ast.AST;
import org.tlang.ast.ASTList;
import org.tlang.context.TypeTable;
import org.tlang.context.ValueTable;
import org.tlang.exception.EvalException;
import org.tlang.exception.TypeException;
import org.tlang.type.BoolType;
import org.tlang.type.Type;

import java.util.List;

public class IfStatement extends ASTList {
    public IfStatement(List<AST> children) {
        super(children);
    }

    public AST condition() {
        return child(0);
    }

    public AST ifBlock() {
        return child(1);
    }

    public AST elseBlock() {
        return numChildren() > 2 ? child(2) : null;
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder("(if ")
                .append(condition()).append(" ")
                .append(ifBlock());
        if (elseBlock() != null) {
            stringBuilder.append(" else ").append(elseBlock()).append(")");
        }
        return stringBuilder.toString();
    }

    @Override
    public Type typeCheck(TypeTable typeTable) throws TypeException {
        Type conditionType = condition().typeCheck(typeTable);
        conditionType.assertSubTypeOf(BoolType.BOOL_TYPE, typeTable, this);
        Type ifType = ifBlock().typeCheck(typeTable);
        if (elseBlock() != null) {
            Type elseType = elseBlock().typeCheck(typeTable);
            elseType.assertTypeOf(ifType, elseBlock());
        }
        return ifType;
    }

    @Override
    public Object eval(ValueTable valueTable) {
        Object condition = condition().eval(valueTable);
        if (!(condition instanceof Boolean)) {
            throw new EvalException("condition expression eval result is not boolean", condition());
        }

        if ((Boolean) condition) {
            return ifBlock().eval(valueTable);
        }

        if (elseBlock() != null) {
            return elseBlock().eval(valueTable);
        }

        return null;
    }
}
